package slap.mvc;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import slap.http.Request;
import slap.http.Response;
import slap.paradigm.Provider;

import jregex.Matcher;
import jregex.Pattern;

public class Action {
	
	public static final String MATCHER_RESULT = "__matcher__result";
	
	private static final Logger logger = LoggerFactory.getLogger(Action.class);
	
	private String direct = "";
	private String wildcard = "";
	private Pattern regex = new Pattern("");
	
	private Method actionMethod;
	private Controller controller;
	private String[] httpMethods;
	
	private ActionMethodParametersProvider ampp = new ActionMethodParametersProvider();
	
	public String direct(){
		return direct;
	}
	public String wildcard(){
		return wildcard;
	}
	public Pattern regex(){
		return regex;
	}
	
	public Method actionMethod(){
		return actionMethod;
	}
	public Controller controller(){
		return controller;
		
	}
	public String[] httpMethods(){
		return httpMethods;
	}
	
	public Action(Method actionMethod, Controller controller){
		this.actionMethod = actionMethod;
		this.controller = controller;
	}
	
	public Action serveDirect(String direct){
		this.direct = direct;
		return this;
	}
	
	public Action serveWildcard(String wildcard){
		this.wildcard = wildcard;
		return this;
	}
	
	public Action serveRegex(Pattern regex){
		this.regex = regex;
		Class<?>[] paramsTypes = actionMethod.getParameterTypes();
		for(int i=0;i<paramsTypes.length;i++){
			if(paramsTypes[i] == Matcher.class){
				ampp.insert(i, new Provider.Provider0<Matcher>(){
					@Override
					public Matcher produce() {
						return Request.current().get(MATCHER_RESULT, Matcher.class);
					}
				});
			}
		}
		return this;
	}
	
	public Action allow(String... httpMethods){
		this.httpMethods = httpMethods;
		return this;
	}
	
	public void excute(Request request, Response response) throws Exception {
		try {
			actionMethod.invoke(controller, ampp.produce());
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
	class ActionMethodParametersProvider implements Provider.Provider0<Object[]>{
		
		private Provider.Provider0<?>[] providers = new Provider.Provider0<?>[6];
		
		private int last = -1;
		
		public void insert(int position, Provider.Provider0<?> provider){
			last = Math.max(last, position);
			if(last > 6){
				Provider.Provider0<?>[] temp = new Provider.Provider0<?>[last+4];
				for(int i=0;i<temp.length;i++){
					temp[i] = providers[i];
				}
				providers = null;
				providers = temp;
			}
			providers[position] = provider;
		}
		
		@Override
		public Object[] produce() {
			if(last < 0){
				return null;
			}
			
			Object[] result = new Object[last+1];
			
			for(int i=0;i<last+1;i++){
				result[i] = providers[i].produce();
			}
			return result;
		}
		
	}
	
}
